const DRAWING = "drawing";
const UPDATING = "updating";


const LEFT_SIDE = "left";
const RIGHT_SIDE = "right";
const USE_BEZIER_FIT = "useBezierFit";
const USE_BSPLINE_FIT = "useBSplieFit";

class AreaPlotBase {


  constructor(polets) {
    this.plotState = DRAWING;
    this.polets = polets || [];
  }

  getControlPoints() { //克隆复制
    // List <  > polets = new List <  > ();
    // polets.pushRange(this.polets);
    return this.polets;
  }

  setControlPoints(value) {
    this.polets = value;
    this.create();
  }

  pushPoint(polet) {
    this.polets.push(polet);
    this.create();
  }

  popPoint() {
    if (this.polets.length - 1 >= 0) {
      this.polets.splice(this.polets.length - 1);
      this.create();
    }
  }

  get_pointCount() {
    return this.polets.length;
  }

  get_minPointlength() {
    return 2;
  }

  finishDraw() {
    this.plotState = UPDATING;
  }

  updateControlPoint(polet, index) {
    if (index >= 0 && index < this.get_pointCount()) {
      this.polets[index] = polet;
    }
    this.create();
  }

  create() {
    // Abstract Method
  }


  /**
  * Method: cloneControlPoints
  * 克隆控制点数组
  *
  */
  static cloneControlPoints(cp) {
    var controlPoints = [];
    for (var i = 0; i < cp.length; i++) {
      controlPoints.push(cp[i]);//.clone()
    }
    return controlPoints;
  }
  /**tool*/
  getArrowPlot(inpolets, hasSwallowTail = false, swallowTailFactor = 0, curveFitMethod = USE_BEZIER_FIT, headHeightFactor = 0.8, headWidthFactor = 0.4, neckHeightFactor = 0.75, neckWidthFactor = 0.15, tailWidthFactor = 0.5) {
    // 将点转换到屏幕坐标
    let polets = [];
    polets = inpolets;
    // 获取组成箭头的五个点（顺时针）
    const arrowPolets = this.getArrowHeadPoints(polets, headHeightFactor, headWidthFactor, neckHeightFactor, neckWidthFactor);
    // 箭颈左右点
    let neckLeftPolet = arrowPolets[0];
    let neckRightPolet = arrowPolets[4];
    // 获取箭身(顺时针）
    let bodyPolets = this.getArrowBodyPoints(polets, neckLeftPolet, neckRightPolet, tailWidthFactor);
    // 获取箭尾
    let tailPolets = this.getArrowTailPoints(polets, tailWidthFactor, hasSwallowTail, swallowTailFactor);
    let tailLeftPolet = tailPolets[0];
    let swallowTailPolet = tailPolets.length == 3 ? tailPolets[1] : {
      X: ((tailPolets[0].X + tailPolets[1].X) / 2),
      Y: ((tailPolets[0].Y + tailPolets[1].Y) / 2)
    };
    let tailRightPolet = tailPolets.length == 3 ? tailPolets[2] : tailPolets[1];
    // 将箭颈点、箭身点、箭尾点合并
    let n = bodyPolets.length;
    let lPolets = bodyPolets.GetRange(0, n / 2); //TODO
    let rPolets = bodyPolets.GetRange(n / 2, n - n / 2);
    // 合并箭颈点
    lPolets.push(neckLeftPolet);
    rPolets.push(neckRightPolet);
    // 合并箭尾点
    lPolets.reverse();
    lPolets.push(tailLeftPolet);
    rPolets.reverse();
    rPolets.push(tailRightPolet);
    // 曲线拟合
    if (curveFitMethod == USE_BEZIER_FIT) {
      lPolets = this.getBezierPoints(lPolets);
      rPolets = this.getBezierPoints(rPolets);
    }
    else {
      lPolets = this.getBSplinePoints(lPolets);
      rPolets = this.getBSplinePoints(rPolets);
    }
    // 添加箭尾点
    if (swallowTailPolet != null) {
      lPolets.push(swallowTailPolet);
      rPolets.push(swallowTailPolet);
    }
    // 将所有点调整顺序、合并、转换到地图坐标并返回
    lPolets.reverse();
    lPolets.push(arrowPolets);
    lPolets.push(rPolets);
    return lPolets;
  }

  getArrowHeadPoints(polets, headHeightFactor, headWidthFactor, neckHeightFactor, neckWidthFactor) {
    let allLen = this.getBaseLength(polets);
    let headHeight = allLen * headHeightFactor;
    let headWidth = headHeight * headWidthFactor;
    let neckWidth = headHeight * neckWidthFactor;

    let n = polets.length;
    let headPnt = polets[n - 1];

    // 如果最后两个点之间的距离小于headHeight，则使用这两个点之间的距离作为headHeight
    let len = this.distance(headPnt, polets[n - 2]);
    headHeight = headHeight > len ? len : headHeight;
    let neckHeight = headHeight * neckHeightFactor;

    let headEndPnt = this.getThirdPoint(polets[n - 2], headPnt, 0, headHeight, LEFT_SIDE);
    let neckEndPnt = this.getThirdPoint(polets[n - 2], headPnt, 0, neckHeight, LEFT_SIDE);

    let headLeftPnt = this.getThirdPoint(headPnt, headEndPnt, Math.PI * 1.5, headWidth, RIGHT_SIDE);
    let neckLeftPnt = this.getThirdPoint(headPnt, neckEndPnt, Math.PI * 1.5, neckWidth, RIGHT_SIDE);
    let headRightPnt = this.getThirdPoint(headPnt, headEndPnt, Math.PI * 1.5, headWidth, LEFT_SIDE);
    let neckRightPnt = this.getThirdPoint(headPnt, neckEndPnt, Math.PI * 1.5, neckWidth, LEFT_SIDE);
    let headPolets = [];
    headPolets.push(neckLeftPnt);
    headPolets.push(headLeftPnt);
    headPolets.push(headPnt);
    headPolets.push(headRightPnt);
    headPolets.push(neckRightPnt);
    return headPolets;
  }

  getArrowBodyPoints(polets, neckLeftPolet, neckRightPolet, tailWidthFactor, leftFactor = 1, rightFactor = 1) {
    let allLen = this.wholeDistance(polets);
    let len = this.getBaseLength(polets);
    let tailWidth = len * tailWidthFactor;
    let neckWidth = this.distance(neckLeftPolet, neckRightPolet);
    let widthDif = tailWidth - neckWidth / 2;
    let tempLen = 0;
    let lPolets = [];
    let rPolets = [];
    for (let j = 1; j < polets.length - 1; j++) {
      let angle = this.getAngleOfThreePoints(polets[j - 1], polets[j], polets[j + 1]) / 2;
      tempLen += this.distance(polets[j - 1], polets[j]);
      let w = (tailWidth - tempLen / allLen * widthDif) / Math.sin(angle) / 2;
      lPolets.push(this.getThirdPoint(polets[j - 1], polets[j], angle, w * leftFactor, RIGHT_SIDE));
      rPolets.push(this.getThirdPoint(polets[j - 1], polets[j], Math.PI - angle, w * rightFactor, LEFT_SIDE));
    }
    for (let i = 0; i < rPolets.length; i++) {
      lPolets.push(rPolets[i]);
    }
    return lPolets;
  }

  getArrowTailPoints(polets, tailWidthFactor, hasSwallowTail = false, swallowTailFactor = 0) {
    let allLen = this.getBaseLength(polets);
    let tailWidth = allLen * tailWidthFactor;
    let tailPolets = [];
    let leftTailPolet = this.getThirdPoint(polets[1], polets[0], Math.PI * 1.5, tailWidth, RIGHT_SIDE);
    let rightTailPolet = this.getThirdPoint(polets[1], polets[0], Math.PI * 1.5, tailWidth, LEFT_SIDE);
    if (hasSwallowTail) {
      let swallowLen = tailWidth * swallowTailFactor;
      let swallowPolet = this.getThirdPoint(polets[1], polets[0], 0, swallowLen, LEFT_SIDE);
      tailPolets.push(leftTailPolet);
      tailPolets.push(swallowPolet);
      tailPolets.push(rightTailPolet);
      return tailPolets;
    }
    tailPolets.push(leftTailPolet);
    tailPolets.push(rightTailPolet);
    return tailPolets;
  }

  getBezierPoints(polets) {
    if (polets.length <= 2)
      return polets;

    const bezierPolets = [];
    let n = polets.length - 1;
    for (let t = 0; t <= 1; t += 0.01) {
      let x = 0;
      let y = 0;
      for (let index = 0; index <= n; index++) {
        // 通过二项式系数计算系数
        let factor = this.getBinomialFactor(n, index);
        let a = Math.pow(t, index);
        let b = Math.pow((1 - t), (n - index));
        x += factor * a * b * polets[index].X;
        y += factor * a * b * polets[index].Y;
      }
      bezierPolets.push({ X: x, Y: y });
    }
    bezierPolets.push(polets[n]);
    return bezierPolets;
  }

  getBSplinePoints(polets, n = 2) {
    if (polets.length <= 2 || polets.length <= n)
      return polets;
    const bSplinePolets = [];
    // 分段数
    let m = polets.length - n - 1;
    bSplinePolets.push(polets[0]);
    for (let i = 0; i <= m; i++) {
      for (let t = 0; t <= 1; t += 0.05) {
        let x = 0;
        let y = 0;
        for (let k = 0; k <= n; k++) {
          // 通过B样条基函数计算系数
          let factor = this.getBSplineFFactor(k, n, t);
          x += factor * polets[i + k].X;
          y += factor * polets[i + k].Y;
        }
        bSplinePolets.push(new (x, y));
      }
    }
    bSplinePolets.push(polets[polets.length - 1]);
    return bSplinePolets;
  }

  getBSplineFFactor(k, n, t) {
    if (n == 2)
      return this.getQuadricBSplineFactor(k, t);

    let value = 0;
    // n的阶乘
    let nFactorial = this.getFactorial(n);
    for (let j = 0; j <= n - k; j++) {
      // 根据公式进行计算
      let f = (j % 2 == 0 ? 1 : -1);
      value += f * this.getBinomialFactor(n + 1, j) * Math.pow(t + n - k - j, n);
    }
    return value / nFactorial;
  }

  getQuadricBSplineFactor(k, t) {
    if (k == 0)
      return Math.pow(t - 1, 2) / 2;
    if (k == 1)
      return (-2 * Math.pow(t, 2) + 2 * t + 1) / 2;
    if (k == 2)
      return Math.pow(t, 2) / 2;
    return 0;
  }

  getBinomialFactor(n, index) {
    return this.getFactorial(n) / (this.getFactorial(index) * this.getFactorial(n - index));
  }

  getFactorial(n) {
    if (n <= 1)
      return 1;
    if (n == 2)
      return 2;
    if (n == 3)
      return 6;
    if (n == 4)
      return 24;
    if (n == 5)
      return 120;
    let result = 1;
    for (let i = 1; i <= n; i++)
      result *= i;
    return result;
  }

  getBaseLength(polets) {
    let distance = this.wholeDistance(polets);
    //return 2 * Math.sqrt(distance);
    return distance;
  }

  distance(pnt1, pnt2) {
    return Math.sqrt(Math.pow((pnt1.X - pnt2.X), 2) + Math.pow((pnt1.Y - pnt2.Y), 2));
  }

  wholeDistance(polets) {
    if (polets.length <= 1)
      return 0;
    let value = 0;
    for (let i = 0; i < polets.length - 1; i++)
      value += this.distance(polets[i], polets[i + 1]);
    return value;
  }

  getThirdPoint(startPnt, endPnt, angle, length, side) {
    let azimuth = this.getAzimuthAngle(startPnt, endPnt);
    let alpha = 0;
    if (side == LEFT_SIDE)
      alpha = azimuth + angle;
    else
      alpha = azimuth - angle;
    let dx = length * Math.cos(alpha);
    let dy = length * Math.sin(alpha);
    return { X: endPnt.X + dx, Y: endPnt.Y + dy };
  }

  getAngleOfThreePoints(pntA, pntB, pntC) {
    let angle = this.getAzimuthAngle(pntB, pntA) - this.getAzimuthAngle(pntB, pntC);
    if (angle < 0)
      angle = angle + Math.PI * 2;
    return angle;
  }

  getAzimuthAngle(startPnt, endPnt) {
    let azimuth = 0.0;
    let angle = Math.asin(Math.abs(endPnt.Y - startPnt.Y) / this.distance(startPnt, endPnt));
    if (endPnt.Y >= startPnt.Y && endPnt.X >= startPnt.X)
      azimuth = angle + Math.PI;
    else if (endPnt.Y >= startPnt.Y && endPnt.X < startPnt.X)
      azimuth = 2 * Math.PI - angle;
    else if (endPnt.Y < startPnt.Y && endPnt.X < startPnt.X)
      azimuth = angle;
    else if (endPnt.Y < startPnt.Y && endPnt.X >= startPnt.X)
      azimuth = Math.PI - angle;
    return azimuth;
  }

  getMidPoint(pnt1, pnt2) {
    return new ((pnt1.X + pnt2.X) / 2, (pnt1.Y + pnt2.Y) / 2);
  }

  getSide(pnt1, pnt2, pnt) {
    let flag = (pnt2.Y - pnt1.Y) * (pnt.X - pnt1.X) - (pnt.Y - pnt1.Y) * (pnt2.X - pnt1.X);
    if (flag > 0)
      return LEFT_SIDE;
    else if (flag < 0)
      return RIGHT_SIDE;
    return null;
  }

  getAdvancedBezierPoints(inpolets) {
    let polets = inpolets || [];
    //polets.push(inpolets);
    let n = polets.length;
    polets.push(polets[0]);
    // get A polets
    let aPnts = [];
    for (let i = 0; i < n; i++)
      aPnts.push(this.getMidPoint(polets[i], polets[i + 1]));

    aPnts.push(aPnts[0]);
    polets.push(polets[1]);
    // get B polets
    let bPnts = [];
    for (let i = 0; i < n; i++) {
      let l1 = this.distance(polets[i], polets[i + 1]);
      let l2 = this.distance(polets[i + 1], polets[i + 2]);
      let d12 = this.distance(aPnts[i], aPnts[i + 1]);
      let d1 = d12 * l1 / (l1 + l2);
      bPnts.push(this.getThirdPoint(aPnts[i + 1], aPnts[i], 0, d1, LEFT_SIDE));
    }

    // move B polets
    let mPnts = [];
    for (let i = 0; i < n; i++) {
      let dx = polets[i + 1].X - bPnts[i].X;
      let dy = polets[i + 1].Y - bPnts[i].Y;
      mPnts.push({ X: aPnts[i].X + dx, Y: aPnts[i].Y + dy });
      mPnts.push(polets[i + 1]);
      mPnts.push({ X: aPnts[i + 1].X + dx, Y: aPnts[i + 1].Y + dy });
    }

    let bezierPnts = [];
    let pnts = [].concat(mPnts);
    //pnts.pushRange(mPnts);
    pnts.push(mPnts[0]);
    pnts.push(mPnts[1]);
    for (let i = 1; i < pnts.length - 1; i += 3) {
      bezierPnts = bezierPnts.concat(this.getBezierPoints(pnts.slice(i, i + 4)));
    }

    return bezierPnts;
  }

  getEquidistantPoint(inpolets, Spacing) {
    let list = [];
    for (let i = 0; i < (inpolets.length - 1); i++) {
      list = list.concat(this.getEquidistant(inpolets[i], inpolets[i + 1], Spacing));
    }
    return list;
  }

  getConvexHullPoint(startPnt, endPnt, Spacing) {
    let list = [];
    let angle = this.getPolarAngle360(startPnt, endPnt) + 90.0;
    list.push(startPnt);
    list.push(this.getPolarPointOfCenterPoint(startPnt, angle, Spacing));
    list.push(this.getPolarPointOfCenterPoint(endPnt, angle, Spacing));
    list.push(endPnt);
    return list;
  }

  getEquidistant(startPnt, endPnt, Spacing) {
    let num2 = this.distance(startPnt, endPnt) / Spacing;
    let angle = this.getPolarAngle360(startPnt, endPnt);
    let list = [];
    list.push(startPnt);
    for (let i = 0; i < num2; i++) {
      list.push(this.getPolarPointOfCenterPoint(list[i], angle, Spacing));
    }
    return list;
  }

  getPolarAngle360(startpolet, endpolet) {
    let x = endpolet.X - startpolet.X;
    let y = endpolet.Y - startpolet.Y;
    return ((Math.atan2(y, x) * 180.0) / 3.1415926535897931);
  }

  getPolarPointOfCenterPoint(centerPnt, angle, length) {
    let num = length * Math.cos(0.017453292519943295 * angle);
    let num2 = length * Math.sin(0.017453292519943295 * angle);
    return { X: centerPnt.X + num, Y: centerPnt.Y + num2 };
  }

  getAzimuthAngle360(startPnt, endPnt) {
    let num = 0.0;
    let num2 = Math.asin(Math.abs((endPnt.Y - startPnt.Y)) / this.distance(startPnt, endPnt));
    if ((endPnt.Y >= startPnt.Y) && (endPnt.X >= startPnt.X)) {
      num = num2 + 3.1415926535897931;
    }
    else if ((endPnt.Y >= startPnt.Y) && (endPnt.X < startPnt.X)) {
      num = 6.2831853071795862 - num2;
    }
    else if ((endPnt.Y < startPnt.Y) && (endPnt.X < startPnt.X)) {
      num = num2;
    }
    else if ((endPnt.Y < startPnt.Y) && (endPnt.X >= startPnt.X)) {
      num = 3.1415926535897931 - num2;
    }
    return ((num * 180.0) / 3.1415926535897931);
  }

  getCurvePoint(startPnt, endPnt) {
    let pnts = [];
    let num = this.distance(startPnt, endPnt) / 15.0;
    for (let i = 0.0; i < 15.707963267948966; i += 0.01) {
      let y = Math.sin(i) * num;
      pnts.push(new (i * num, y));
    }
    let num4 = this.getPolarAngle360(startPnt, endPnt);
    let list2 = this.getRotatePoint(pnts, num4);
    let list3 = [];
    for (let polet in list2) {
      list3.push({ X: startPnt.X + polet.X, Y: startPnt.Y + polet.Y });
    }
    return list3;
  }

  getRotatePoint(pnts, angle360) {
    let list = [];
    for (let polet in pnts) {
      let x = (polet.X * Math.cos((angle360 * 3.1415926535897931) / 180.0)) - (polet.Y * Math.sin((angle360 * 3.1415926535897931) / 180.0));
      let y = (polet.X * Math.sin((angle360 * 3.1415926535897931) / 180.0)) + (polet.Y * Math.cos((angle360 * 3.1415926535897931) / 180.0));
      list.push(new (x, y));
    }
    return list;
  }

  /**
          * Method: calculateVector
          * 计算和基准向量v夹角为a、长度为d的目标向量（理论上有两个，一左一右）
          *
          * Parameters:
          * v - {<Point>} 基准向量
          * a - {Number} 目标向量和基准向量的夹角，默认为90度，这里的单位使用弧度
          * d - {Number} 目标向量的长度，即模，默认为1，即单位向量
          *
          * Returns:
          * {Array(<Point>)} 回目标向量数组（就两个向量，一左一右）:Point
          */
  static calculateVector(v, a, d) {
    if (!a) a = Math.PI / 2;
    if (!d) d = 1;

    //定义目标向量的头部   x 坐标
    var x_1;
    var x_2;
    //定义目标向量的头部   y 坐标
    var y_1;
    var y_2;
    //定义目标向量，一左一右
    var v_l;
    var v_r;

    //计算基准向量v的模
    var d_v = Math.sqrt(v.x * v.x + v.y * v.y);

    //基准向量的斜率为0时，y值不能作为除数，所以需要特别处理
    if (v.y == 0) {
      //计算x,会有两个值
      x_1 = x_2 = d_v * d * Math.cos(a) / v.x;
      //根据v.x的正负判断目标向量的左右之分
      if (v.x > 0) {
        //计算y
        y_1 = Math.sqrt(d * d - x_1 * x_1);
        y_2 = -y_1;
      }
      else if (v.x < 0) {
        //计算y
        y_2 = Math.sqrt(d * d - x_1 * x_1);
        y_1 = -y_2;

      }
      v_l = { x: x_1, y: y_1 };
      v_r = { x: x_2, y: y_2 };
    }
    //此为大多数情况
    else {
      //转换为y=nx+m形式
      var n = -v.x / v.y;
      var m = d * d_v * Math.cos(a) / v.y;
      //
      //x*x + y*y = d*d
      //转换为a*x*x + b*x + c = 0
      var a = 1 + n * n;
      var b = 2 * n * m;
      var c = m * m - d * d;
      //计算x,会有两个值
      x_1 = (-b - Math.sqrt(b * b - 4 * a * c)) / (2 * a);
      x_2 = (-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a);
      //计算y
      y_1 = n * x_1 + m;
      y_2 = n * x_2 + m;
      //当向量向上时
      if (v.y >= 0) {
        v_l = { x: x_1, y: y_1 };
        v_r = { x: x_2, y: y_2 };
      }
      //当向量向下时
      else if (v.y < 0) {
        v_l = { x: x_2, y: y_2 };
        v_r = { x: x_1, y: y_1 };
      }
    }
    return [v_l, v_r];
  }

  /**
         * Method: calculateIntersection
         * 计算两条直线的交点
         * 通过向量的思想进行计算，需要提供两个向量以及两条直线上各自一个点
         *
         * Parameters:
         * v_1 - {<Point>} 直线1的向量
         * v_2 - {<Point>} 直线2的向量
         * points1 - {<Point>} 直线1上的任意一点
         * points2 - {<Point>} 直线2上的任意一点
         *
         * Returns:
         * {Array(<SuperMap.Geometry.Point>)} 返回交点
         */
  static calculateIntersection(v_1, v_2, point1, point2) {
    //定义交点的坐标
    var x;
    var y;
    //如果向量v_1和v_2平行
    if (v_1.y * v_2.x - v_1.x * v_2.y == 0) {
      //平行也有两种情况
      //同向
      if (v_1.x * v_2.x > 0 || v_1.y * v_2.y > 0) {
        //同向直接取两个点的中点
        x = (point1.x + point2.x) / 2;
        y = (point1.y + point2.y) / 2;
      }
      //反向
      else {
        //如果反向直接返回后面的点位置
        x = point2.x;
        y = point2.y;
      }
    }
    else {
      //
      x = (v_1.x * v_2.x * (point2.y - point1.y) + point1.x * v_1.y * v_2.x - point2.x * v_2.y * v_1.x) / (v_1.y * v_2.x - v_1.x * v_2.y);
      if (v_1.x != 0) {
        y = (x - point1.x) * v_1.y / v_1.x + point1.y;
      }
      //不可能v_1.x和v_2.x同时为0
      else {
        y = (x - point2.x) * v_2.y / v_2.x + point2.y;
      }
    }
    return { x: x, y: y };

  }


  /**
   * Method: calculatePointsFBZ2
   * 计算2次贝塞尔曲线的点
   *
   * Parameters:
   * points -{Array(<Point>)} 传入的待计算的初始点串（必须为三个点）。
   * part -{Number} 平滑度。取值越大，曲线越平滑。取值为大于1的整数。
   * Returns:
   * {Array(<Point>)} 2次贝塞尔曲线的所有点
   */
  static calculatePointsFBZ2(points, part) {
    if (!part) part = 20;

    //获取待拆分的点
    var bezierPts = [];
    var scale = 0.05;
    if (part > 0) {
      scale = 1 / part;
    }

    for (var i = 0; i < points.length - 2;) {
      //起始点
      var pointS = points[i];
      //控制点
      var pointC = points[i + 1];
      //结束点
      var pointE = points[i + 2];

      bezierPts.push(pointS);
      for (var t = 0; t < 1;) {
        //二次贝塞尔曲线公式
        var x = (1 - t) * (1 - t) * pointS.x + 2 * t * (1 - t) * pointC.x + t * t * pointE.x;
        var y = (1 - t) * (1 - t) * pointS.y + 2 * t * (1 - t) * pointC.y + t * t * pointE.y;
        var point = { x: x, y: y };
        bezierPts.push(point);
        t += scale;
      }

      i += 2;
      if (i >= points.length) {
        bezierPts.push(pointS);
      }
    }

    //需要判定一下最后一个点是否存在
    var poRE = bezierPts[bezierPts.length - 1];
    var popE = points[points.length - 1];
    //if (!poRE.equals(poRE)) { 
    if (!this.cmp(poRE, poRE)) {
      bezierPts.push(popE.clone());
    }

    return bezierPts;
  }

  /**
  *判断两个结构体是否相同
  **/
  static cmp(x, y) {
    // If both x and y are null or undefined and exactly the same 
    if (x === y) {
      return true;
    }

    // If they are not strictly equal, they both need to be Objects 
    if (!(x instanceof Object) || !(y instanceof Object)) {
      return false;
    }

    //They must have the exact same prototype chain,the closest we can do is
    //test the constructor. 
    if (x.constructor !== y.constructor) {
      return false;
    }

    for (var p in x) {
      //Inherited properties were tested using x.constructor === y.constructor
      if (x.hasOwnProperty(p)) {
        // Allows comparing x[ p ] and y[ p ] when set to undefined 
        if (!y.hasOwnProperty(p)) {
          return false;
        }

        // If they have the same strict value or identity then they are equal 
        if (x[p] === y[p]) {
          continue;
        }

        // Numbers, Strings, Functions, Booleans must be strictly equal 
        if (typeof (x[p]) !== "object") {
          return false;
        }

        // Objects and Arrays must be tested recursively 
        if (!Object.equals(x[p], y[p])) {
          return false;
        }
      }
    }

    for (p in y) {
      // allows x[ p ] to be set to undefined 
      if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Method: calculateAngularBisector
   * 计算两个向量的角平分线向量
   *
   * Parameters:
   * v1 - {<Point>} 向量1
   * v2 - {<Point>} 向量2
   *
   * Returns:
   * {Array(<Point>)} 返回角平分线向量
   */
  static calculateAngularBisector(v1, v2) {
    //计算角平分线的思想是取两个向量的单位向量，然后相加
    var d1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
    var d2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);

    return { x: v1.x / d1 + v2.x / d2, y: v1.y / d1 + v2.y / d2 };
  }


  /**
    * Method: calculateIntersectionFromTwoCorner——双箭头
    * 通过三角形的底边两端点坐标以及底边两夹角，计算第三个点坐标
    *
    * Parameters:
    * pointS - {<Point>} 底边第一个点
    * pointE - {<Point>} 底边第二个点
    * a_S - {Number} 底边和第一个点所在的另一条边的夹角
    * a_E - {Number} 底边和第二个点所在的另一条边的夹角
    *
    * Returns:
    * {Array(<Point>)} 返回顶点（理论上存在两个值）
    */
  static calculateIntersectionFromTwoCorner(pointS, pointE, a_S, a_E) {
    if (!a_S) a_S = Math.PI / 4;
    if (!a_E) a_E = Math.PI / 4;

    //起始点、结束点、交点加起来三个点，形成一个三角形
    //斜边（起始点到结束点）的向量为
    var v_SE = new SuperMap.Geometry.Point(pointE.x - pointS.x, pointE.y - pointS.y);
    //计算起始点、交点的单位向量
    var v_SI_lr = this.calculateVector(v_SE, a_S, 1);
    //获取
    var v_SI_l = v_SI_lr[0];
    var v_SI_r = v_SI_lr[1];
    //计算结束点、交点的单位向量
    var v_EI_lr = this.calculateVector(v_SE, Math.PI - a_S, 1);
    //获取
    var v_EI_l = v_EI_lr[0];
    var v_EI_r = v_EI_lr[1];
    //求左边的交点
    var pointI_l = this.calculateIntersection(v_SI_l, v_EI_l, pointS, pointE);
    //计算右边的交点
    var pointI_r = this.calculateIntersection(v_SI_r, v_EI_r, pointS, pointE);
    return [pointI_l, pointI_r];
  }



  /**
* Method: createBezier1
* 创建1次贝塞尔曲线。
*
* Parameters:
* points -{Array(<Point>)} 传入的待计算的初始点串。
* precision -{Number} 拆分精度，表示贝塞尔曲线上任意两点间横向或纵向的最大距离。
*                     决定贝塞尔曲线的平滑程度。取值越小曲线越平滑。取值为大于1的整数。
* part -{Number} 平滑度。取值越大，曲线越平滑。取值为大于1的整数。
* Returns:
* {SuperMap.Geometry.LineString} 几何线对象
*
* (code)
* var points = [];
* points.push(new SuperMap.Geometry.Point(-50,30));
* points.push(new SuperMap.Geometry.Point(-30,50));
* points.push(new SuperMap.Geometry.Point(2,60));
* points.push(new SuperMap.Geometry.Point(8,20));
*
* var bezier = SuperMap.Geometry.LineString.createBezier1(points, 20);
* (end)
*/
  static createBezier1(points, precision, part) {
    if (part) {
      return this.calculatePointsFBZ3(points, part);
    }
    //获取待拆分的点
    var bezierPts = [];
    for (var m = 0; m < points.length; m++) {
      bezierPts[m] = points[m];
    }
    //获取输入点的数量
    var i;
    var k;
    var j = 0;
    var bExit;
    var count = bezierPts.length;
    var ptBuffer = [];
    var ok = true;
    while (ok) {
      bExit = true;
      //贝塞尔分解是按4个点为一组进行的，所以小于4个点就再不进行分解
      for (i = 0; i < count - 3; i += 3) {
        //对输入点数组进行分解
        //判断bezierPts[i]到bezierPts[i+4]是否达到精度
        if (GetBezierGap(bezierPts, i) > precision) {
          bExit = false;
          //对未达到精度的bezierPts[i]到bezierPts[i+4]进行计算，得到新的ptBuffer点数组
          InciseBezier(bezierPts, i, ptBuffer);
          //去除已使用过的2个控制点
          bezierPts.splice(i + 1, 2);
          //将本次计算得到的5个新的点插入到bezierPts[i]位置之后，得到新的bezierPts点数组
          for (k = 0; k < 5; k++) {
            bezierPts.splice(i + 1 + k, 0, ptBuffer[k + 1]);
          }
          //bezierPts[i]到bezierPts[i+4]没有达到精度，所以不能跳过，i需回归初始
          i -= 3;
          count = bezierPts.length;
        }
        if (bExit)
          break;
      }
      //对分解得出的新bezierPts点数组进行优化，除去相同的点
      while (j < count - 1) {
        if (bezierPts[j] === bezierPts[j + 1]) {
          bezierPts.splice(j + 1, 1);
          count--;
        }
        j++;
      }
      ok = false;
    }

    return bezierPts;
  }


  /**
   * Method: calculatePointsFBZ3
   * 计算3次贝塞尔曲线的点
   *
   * points -{Array(<Point>)} 传入的待计算的初始点串(四个)。
   * part -{Number} 平滑度。取值越大，曲线越平滑。取值为大于1的整数。
   *
   * Returns:
   * {Array(<Point>)} 3次贝塞尔曲线的所有点
   */
  static calculatePointsFBZ3(points, part) {
    if (!part) part = 20;
    //获取待拆分的点
    var bezierPts = [];
    var scale = 0.05;

    if (part > 0) {
      scale = 1 / part;
    }

    for (var i = 0; i < points.length - 3;) {
      //起始点
      var pointS = points[i];
      //第一个控制点
      var pointC1 = points[i + 1];
      //第二个控制点
      var pointC2 = points[i + 2];
      //结束点
      var pointE = points[i + 3];

      bezierPts.push(pointS);
      for (var t = 0; t < 1;) {
        //三次贝塞尔曲线公式
        var x = (1 - t) * (1 - t) * (1 - t) * pointS.x + 3 * t * (1 - t) * (1 - t) * pointC1.x + 3 * t * t * (1 - t) * pointC2.x + t * t * t * pointE.x;
        var y = (1 - t) * (1 - t) * (1 - t) * pointS.y + 3 * t * (1 - t) * (1 - t) * pointC1.y + 3 * t * t * (1 - t) * pointC2.y + t * t * t * pointE.y;
        //var point = new SuperMap.Geometry.Point(x, y);
        bezierPts.push({ x: x, y: y });
        t += scale;
      }

      i += 3;
      if (i >= points.length) {
        bezierPts.push(pointS);
      }
    }

    //需要判定一下最后一个点是否存在
    var poRE = bezierPts[bezierPts.length - 1];
    var popE = points[points.length - 1];

    if (!this.cmp(poRE, popE)) {//!poRE.equals(popE)
      bezierPts.push(popE);//.clone()
    }

    return bezierPts;
  }


  /**
   * Method: GetBezierGap
   * 计算贝赛尔曲线两个顶点的纵向和横向的最大距离，结果用来判断是否达到拆分精度
   *
   * pSrcPt -{Array(<Point>)} 传入的待拆分点数组。
   * j - {Number} 本次拆分的首点位置，从pSrcPt[j]（包括此点）点向后取4个点进行距离计算
   */
  static GetBezierGap(pSrcPt, j) {
    var gap = 0;
    for (var i = 1; i < 4; i++) {
      if (Math.abs(pSrcPt[j + i].x - pSrcPt[j + i - 1].x) > gap)
        gap = Math.abs(pSrcPt[j + i].x - pSrcPt[j + i - 1].x);
      if (Math.abs(pSrcPt[j + i].y - pSrcPt[j + i - 1].y) > gap)
        gap = Math.abs(pSrcPt[j + i].y - pSrcPt[j + i - 1].y);
    }
    return gap;
  }


  /**
   * Method: InciseBezier
   * 拆分贝赛尔曲线单元
   *
   * pSrcPt -{Array(<Point>)} 传入的待拆分点数组。
   * j - {Number} 本次拆分的首点位置，从pSrcPt[j]（包括此点）点向后取4个点进行本次拆分。
   * pDstPt -{Array(<Point>)} 将4个点拆分成7个点，pDstPt是包含此7个点的结果点数组。
   */
  static InciseBezier(pSrcPt, j, pDstPt) {
    var buffer = [];
    buffer[0] = [];
    buffer[1] = [];
    buffer[2] = [];
    var i;
    for (i = 0; i < 3; i++) {
      buffer[0][i] = new SuperMap.Geometry.Point;
      buffer[0][i].x = (pSrcPt[j + i].x + pSrcPt[j + i + 1].x) / 2;
      buffer[0][i].y = (pSrcPt[j + i].y + pSrcPt[j + i + 1].y) / 2;
    }
    for (i = 0; i < 2; i++) {
      buffer[1][i] = new SuperMap.Geometry.Point;
      buffer[1][i].x = (buffer[0][i].x + buffer[0][i + 1].x) / 2;
      buffer[1][i].y = (buffer[0][i].y + buffer[0][i + 1].y) / 2;
    }

    buffer[2][0] = {};
    buffer[2][0].x = (buffer[1][0].x + buffer[1][1].x) / 2;
    buffer[2][0].y = (buffer[1][0].y + buffer[1][1].y) / 2;
    //将输入的四个点拆分成7个点
    pDstPt[0] = pSrcPt[j];
    pDstPt[1] = buffer[0][0];
    pDstPt[2] = buffer[1][0];
    pDstPt[3] = buffer[2][0];
    pDstPt[4] = buffer[1][1];
    pDstPt[5] = buffer[0][2];
    pDstPt[6] = pSrcPt[j + 3];
    return true;
  }



}
export default AreaPlotBase 